<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实现数组高级函数(Implement advanced array functions)</title>
  <!-- 
  在JavaScript中，高阶函数（Higher-Order Function）是指满足以下任一条件的函数：
  1. 接受一个或多个函数作为参数。
  2. 返回一个函数。

  高阶函数是JavaScript函数式编程的重要组成部分，能够提高代码的灵活性和可重用性。
  
  一、定义
  - 接受函数作为参数：高阶函数可以接收其他函数作为输入参数，从而实现更复杂的行为。
  - 返回函数：高阶函数可以返回一个新的函数，这个新函数可以在以后的代码中被调用。

  二、示例
  1. 接受函数作为参数：
    下面的例子展示了一个高阶函数 `operate` ，它接受一个函数和两个数字作为参数，并返回计算结果。
    function operate(a, b, operation) {
      return operation(a, b);
    }

    function add(x, y) {
      return x + y;
    }

    function multiply(x, y) {
      return x * y;
    }

    console.log(operate(5, 3, add));      // 输出: 8
    console.log(operate(5, 3, multiply)); // 输出: 15
  2. 返回函数：

    下面的例子展示了一个高阶函数 `makeCounter` ，它返回一个计数器函数。
    function makeCounter() {
      let count = 0;
      return function() {
        count += 1;
        return count;
      };
    }

    const counter = makeCounter();
    console.log(counter()); // 输出: 1
    console.log(counter()); // 输出: 2
    console.log(counter()); // 输出: 3

  三、高阶函数的应用
  1. 数组方法：许多数组方法（如 `map` 、 `filter` 、 `reduce` ）都是高阶函数，它们接受一个函数作为参数并对数组中的每个元素执行该函数。
  const numbers = [1, 2, 3, 4];
    const doubled = numbers.map(num => num * 2); // 使用高阶函数map
    console.log(doubled); // 输出: [2, 4, 6, 8]
  2. 函数组合：高阶函数可以用于组合多个函数，形成更复杂的操作。

  3. 柯里化（Currying）：高阶函数可以用于实现柯里化，将一个多参数函数转换为一系列单参数函数。

  四、总结
  - 高阶函数是指接受函数作为参数或返回函数的函数。
  - 它们能够提高代码的灵活性和可重用性。
  - 高阶函数在JavaScript中广泛应用于数组操作、函数组合和柯里化等场景。
   -->
</head>
<body>

  <script>
    let arr = ['Implement', 'advanced', 'array', 'functions']
    
    // 1.手动实现 foeEach 函数
    // 1.1 for循环实现
    function myForEach(fn) {
      for (let i = 0; i < this.length; i++) {
        fn(this[i], i, this)
      }
    }

    myForEach.call(arr, function(value, index, arr) {
      console.log("foeEach-for循环实现", value, index, arr)
    })

    myForEach.bind(arr, function(value, index, arr) {
      console.log("foeEach-for循环实现bind", value, index, arr)
    })()

    myForEach.bind(arr)(function(value, index, arr) {
      console.log("foeEach-for循环实现bind2", value, index, arr)
    })

    // 1.2 for...of实现
    function myForEach2(fn) {
      let i = 0
      for (let item of this) {
        fn(item, i++, this)
      }
    }

    myForEach2.apply(arr, [function(value, index, arr) {
      console.log("foeEach-for...of实现", value, index, arr)
    }])

    // 1.3 for本身实现
    arr.myForEach3 = function(fn) {
      for (let i = 0; i < this.length; i++) {
        fn(this[i], i, this)
      }
    }

    arr.myForEach3(function(value, index, arr) {
      console.log("foeEach-for本身实现", value, index, arr)
    })

    // 1.4 原型链实现
    Array.prototype.myForEach4 = function(fn) {
      for (let i = 0; i < this.length; i++) {
        fn(this[i], i, this)
      }
    }

    arr.myForEach4(function(value, index, arr) {
      console.log("foeEach-原型链实现", value, index, arr)
    })

    const info = [
      {
        id: 1,
        name: "张三",
        age: 18
      },
      {
        id: 2,
        name: "李四",
        age: 20
      },
      {
        id: 3,
        name: "王五",
        age: 22
      }
    ]

    // 2.find方法
    // 2.1 for循环实现
    function myFind(fn) {
      for (let i = 0; i < this.length; i++) {
        if (fn(this[i], i, this)) {
          return this[i]
        }
      }
      return undefined
    }

    const findRes1 = myFind.call(info, function(value, index, arr) {
      return value.name === '王五'
    })

    console.log("find-for循环实现", findRes1)

    // 2.2 原型链实现
    Array.prototype.myFind2 = function(fn) {
      for (let i = 0; i < this.length; i++) {
        if (fn(this[i], i, this)) {
          return this[i]
        }
      }
      return undefined
    }

    const findRes2 = info.myFind2(function(value, index, arr) {
      return value.name === '王五'
    })

    console.log("find-原型链实现", findRes2)

    // 3.filter方法
    // 3.1 for循环实现
    function myFilter(fn) {
      const res = []
      for (let i = 0; i < this.length; i++) {
        if (fn(this[i], i, this)) {
          res.push(this[i])
        }
      }
      return res
    }

    const filterRes1 = myFilter.call(info, function(value, index, arr) {
      return value.age > 20
    })

    console.log("filter-for循环实现", filterRes1)

    // 3.2 原型链实现
    Array.prototype.myFilter2 = function(fn) {
      const res = []
      for (let i = 0; i < this.length; i++) {
        if (fn(this[i], i, this)) {
          res.push(this[i])
        }
      }
      return res
    }

    const filterRes2 = info.myFilter2(function(value, index, arr) {
      return value.age > 20
    })

    console.log("filter-原型链实现", filterRes2)

    // 4.reduce方法
    // 4.1 for循环实现
    // initValue为什么是0? 因为reduce的第二个参数是可选的，如果不传，默认是数组的第一个元素
    function myReduce(fn, initValue) {
      let res = initValue
      for (let i = 0; i < this.length; i++) {
        res = fn(res, this[i], i, this)
      }
      return res
    }

    const reduceRes1 = myReduce.call(info, function(prev, cur, index, arr) {
      return prev + cur.age
    }, 0)

    console.log("reduce-for循环实现", reduceRes1)

    // 4.2 原型链实现
    Array.prototype.myReduce2 = function(fn, initValue) {
      let res = initValue
      for (let i = 0; i < this.length; i++) {
        res = fn(res, this[i], i, this)
      }
      return res
    }

    const reduceRes2 = info.myReduce2(function(prev, cur, index, arr) {
      return prev + cur.age
    }, 0)

    console.log("reduce-原型链实现", reduceRes2)

    // 5.map方法
    // 5.1 for循环实现
    function myMap(fn) {
      let res = []
      for (let i = 0; i < this.length; i++) {
        res.push(fn(this[i], i, this))
      }
      return res
    }

    const mapRes1 = myMap.call(info, function(value, index, arr) {
      return value.name
    })

    console.log("map-for循环实现", mapRes1)

    // 5.2 原型链实现
    Array.prototype.myMap2 = function(fn) {
      let res = []
      for (let i = 0; i < this.length; i++) {
        res.push(fn(this[i], i, this))
      }
      return res
    }

    const mapRes2 = info.myMap2(function(value, index, arr) {
      return value.name
    })

    console.log("map-原型链实现", mapRes2)

  </script>
  
</body>
</html>